//g++ -o test test.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#define BUFFER_SIZE 4096  /*读缓冲区大小*/

//主状态机
enum CHECK_STATE
{
	CHECK_STATE_REQUESTLINE = 0, 	//当前正在分析请求行
	CHECK_STATE_HEADER				//当前正在分析头部字段
};

//从状态机，行的读取状态
enum LINE_STATUS
{
	LINE_OK = 0,		//读到完整行
	LINE_BAD,			//行出错
	LINE_OPEN			//行数据不完整
};

//HTTP请求结果
enum HTTP_CODE
{
	NO_REQUEST,				//请求不完整，需要继续读取客户端数据
	GET_REQUEST,			//获得了完整的客户请求
	BAD_REQUEST,			//客户请求有语法错误
	FORBIDDEN_REQUEST,		//客户对资源没有足够的访问权限
	INTERNAL_ERROR,			//服务器内部错误
	CLOSED_CONNECTION		//客户端已关闭连接
};

//发给客户端的应答报文（简化版）
static const char *szret[] = {"I get a correct result\n", "Something wrong\n"};

//从状态机，用于解析出一行内容
LINE_STATUS parse_line(char *buffer, int &checked_index, int &read_index);
//分析请求行
HTTP_CODE parse_requestline(char *temp, CHECK_STATE &checkstate);
//分析头部字段
HTTP_CODE parse_headers(char *temp);
//分析http请求的入口函数
HTTP_CODE parse_content(char *buffer, int &checked_index, CHECK_STATE &checkstate, int &read_index, int &start_index);

int main(int argc, char *argv[])
{
	if (argc != 3) {
		printf("usage: %s ip port\n", basename(argv[0]));
		return 1;
	}
	
	const char *ip = argv[1];
	int port = atoi(argv[2]);
	
	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	inet_pton(AF_INET, ip, &address.sin_addr);
	
	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);
	
	int reuse = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
	
	int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert(ret != -1);
	
	ret = listen(listenfd, 5);
	assert(ret != -1);
	
	struct sockaddr_in client_address;
	socklen_t client_addrlength = sizeof(client_address);
	
	int fd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
	if (fd < 0) 
	{
		printf("errno is: %d\n", errno);
	}
	else 
	{
		char buffer[BUFFER_SIZE];			//读缓冲区
		memset(buffer, '\0', BUFFER_SIZE);
		
		int data_read = 0;					
		int read_index = 0;					//当前已读取的字节数
		int checked_index = 0;				//当前已分析完的字节数
		int start_line = 0;					//buffer中一行的起始位置
		
		//主状态机的初始状态
		CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;
		
		//循环读取客户端数据并分析
		while (1) 
		{
			data_read = recv(fd, buffer + read_index, BUFFER_SIZE - read_index, 0);
			if (data_read == -1) 
			{
				printf("reading failed\n");
				break;
			}
			else if (data_read == 0) 
			{
				printf("remote client has closed the connection\n");
				break;
			}
			
			read_index += data_read;
			
			//分析已读取的数据
			HTTP_CODE result = parse_content(buffer, checked_index, checkstate, read_index, start_line);
			
			if (result == NO_REQUEST) 
			{	//不完整请求
				continue;
			}
			else if (result == GET_REQUEST)
			 {	//完整请求
				send(fd, szret[0], strlen(szret[0]), 0);
				break;
			}
			else 
			{	//其它情况表示发生错误
				send(fd, szret[1], strlen(szret[1]), 0);
				break;
			}
		}
		close(fd);
	}
	close(listenfd);
	return 0;
}

//解析一行内容
LINE_STATUS parse_line(char *buffer, int &checked_index, int &read_index)
{
	char temp;
	//checked_index指向buffer中正在分析的字节，read_index指向buffer中尾部的下一字节
	//0~checked_index已经分析完毕
	for (; checked_index < read_index; ++checked_index) {
		temp = buffer[checked_index]; //获得要分析的字节
		if (temp == '\r')  //如果当前的字节是'\r',即回车符，则有可能读取一个完整的行 
		{
			if ((checked_index + 1) == read_index) 
			{
				return LINE_OPEN;
			}
			else if (buffer[checked_index+1] == '\n')  //读到一个完整行
			{
				buffer[checked_index++] = '\0';
				buffer[checked_index++] = '\0';
				return LINE_OK;
			}
			//否则存在语法错误			
			return LINE_BAD;
		}
		else if (temp == '\n') //也有可能是完整行
		{
			if (checked_index > 1 && buffer[checked_index - 1] == '\r') 
			{
				buffer[checked_index-1] = '\0';
				buffer[checked_index++] = '\0';
				return LINE_OK;
			}
			
			return LINE_BAD;
		}
	}

	//所有内容分析完毕也没有遇到'\r',需要继续读取
	return LINE_OPEN;
}

/*分析请求行
 * 模拟如下：
 * GET / HTTP/1.1
 * Host: www.baidu.com
*/
 HTTP_CODE parse_requestline(char *temp, CHECK_STATE &checkstate)
 {
 	char *url = strpbrk(temp, " \t");
 	if (!url)
 		//没有空格和\t
 		return BAD_REQUEST;

 	*url++ = '\0';

	char *method = temp;		//获取方法
	
	if (strcasecmp(method, "GET") == 0)
		printf("The request method is GET\n");
	else
		return BAD_REQUEST;

	url += strspn(url, " \t");
	char *version = strpbrk(url, " \t");
	if (!version)
		return BAD_REQUEST;

	*version++ = '\0';
	version += strspn(version, " \t");		//获取版本
	
	if (strcasecmp(version, "HTTP/1.1") != 0)	//仅支持http1.1
		return BAD_REQUEST;

	if (strncasecmp(url, "http://", 7) == 0) {	//获取URL
		url += 7;
		url = strchr(url, '/');
	}
	
	if (!url || url[0] != '/')
		return BAD_REQUEST;

	printf("The request URL is: %s\n", url);
	
	checkstate = CHECK_STATE_HEADER;  //请求行处理完毕，状态转移到头部字段分析
	
	return NO_REQUEST;
}

//分析头部字段
HTTP_CODE parse_headers(char *temp)
{
	if (temp[0] == '\0') 
	{
		return GET_REQUEST;		//遇到空行，表示我们得到一个正确的HTTP请求
	}
	else if (strncasecmp(temp, "Host:", 5) == 0) 
	{
		temp += 5;
		temp += strspn(temp, " \t");
		printf("the request host is: %s\n", temp);
	}
	else 
	{	//其它头部字段不处理
		printf("I can not handle this header\n");
	}
	
	return NO_REQUEST;
}

//分析HTTP请求
HTTP_CODE parse_content(char *buffer, int &checked_index, CHECK_STATE &checkstate, int &read_index, int &start_index)
{
	LINE_STATUS linestatus = LINE_OK;	//记录当前行的读取状态
	HTTP_CODE retcode = NO_REQUEST;		//记录HTTP请求的处理结果
	
	//从buffer中取出完整行
	while ((linestatus = parse_line(buffer, checked_index, read_index)) == LINE_OK) 
	{
		char *temp = buffer + start_index;	//start_line是行的起始位置
		start_index = checked_index;				//记录下一行的起始位置
		
		switch(checkstate) 
		{
			case CHECK_STATE_REQUESTLINE:	//分析请求行
			{
				retcode = parse_requestline(temp, checkstate);
				if (retcode == BAD_REQUEST)
					return BAD_REQUEST;

				break;
			}
			case CHECK_STATE_HEADER:			//分析头部字段
			{	
				retcode = parse_headers(temp);
				if (retcode == BAD_REQUEST)
				 {
					return BAD_REQUEST;
				}
				else if(retcode == GET_REQUEST) 
				{
					return GET_REQUEST;
				}
				break;
			}
			default:
			{
				return INTERNAL_ERROR;
			}
			
		}
	}
	
	if (linestatus == LINE_OPEN)		//不完整行，要继续读取客户端数据才能进一步分析
		return NO_REQUEST;
	else
		return BAD_REQUEST;

	return BAD_REQUEST;
}

